﻿// ===============================================================================
// Container.cs
// EOS Web Framework
// ===============================================================================
// Copyright (c) EOS Development Group. All rights reserved.
// ===============================================================================

namespace EOS.Framework.Web.Mvc
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Reflection;
    using EOS.Framework.Kernel;
    using Microsoft.Practices.Unity;
    using System.Web;
    using EOS.Framework.Tools.Aspects;

    /// <summary>
    /// Base class for all webparts and web pages.
    /// </summary>
    public class Container : IContainer
    {
        private const string ApplicationPathKey = "ApplicationPath";
        private const string ActionKey = "Action";
        private const string UrlKey = "Url";
        private bool _loaded;
        private IMarkupCache _cache;
        private static Dictionary<string, int> _staticContainerCounter = new Dictionary<string, int>();

        #region Properties

        private IContainer _parent;

        public IContainer Parent
        {
            get { return _parent; }
            set { _parent = value; }
        }

        private IMarkup _markup;

        public IMarkup Markup
        {
            get { return _markup; }
            set { _markup = value; }
        }

        private PageContext _context;

        public PageContext Context
        {
            get { return _context; }
            protected set { _context = value; }
        }

        private string _name;

        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        #endregion

        /// <summary>
        /// Initializes a new instance of the <see cref="Container"/> class.
        /// </summary>
        public Container()
        {
            _cache = Unity.Current.Resolve<IMarkupCache>();

            LoadMarkup();
            this._markup.Container = this;
        }

        protected virtual void BeginRequest()
        { }

        protected virtual void BeginRender()
        { }

        protected virtual void LoadMarkup()
        {
            if (IsMarkupInCache())
            {
                this._markup = _cache.CreateMarkup(GetType().FullName);

                _loaded = true;
            }
            else
            {
                bool markupFound = false;

                Type type = GetType();

                IMarkupLocator markupLocator = Unity.Current.Resolve<IMarkupLocator>();
                try
                {
                    _markup = markupLocator.LocateMarkup(type, _cache);
                }
                catch (ResolutionFailedException)
                {
                    Assembly assembly = type.Assembly;
                    foreach (string resource in assembly.GetManifestResourceNames())
                    {
                        if (resource.Equals(type.FullName + ".tpl.xml"))
                        {
                            using (Stream stream = assembly.GetManifestResourceStream(resource))
                            {
                                markupFound = true;
                                LoadMarkupFromStream(stream);

                                stream.Close();
                            }
                        }
                    }
                    if (markupFound == false)
                    {
                        throw new MarkupNotFoundException(type.FullName + ".tpl.xml");
                    }
                }
            }
        }

        protected void LoadMarkupFromStream(Stream stream)
        {
            if (_loaded == true)
            {
                throw new InvalidOperationException(Properties.Resources.MarkupAlreadyLoaded);
            }

            using (StreamReader reader = new StreamReader(stream))
            {
                this._markup = _cache.CreateMarkup(reader.ReadToEnd(), GetType().FullName);
            }

            _loaded = true;
        }

        /// <summary>
        /// Loads the markup from the give file.
        /// </summary>
        /// <param name="path">The path of the markup file.</param>
        /// <exception cref="ArgumentNullException"><paramref name="path"/> is null (Nothing in Visual Basic).</exception>
        /// <exception cref="ArgumentException"><paramref name="path"/> is not a valid path.</exception>
        /// <exception cref="InvalidOperationException">Markup is already loaded.</exception>
        protected void LoadMarkupFromFile(string path)
        {
            if (_loaded == true)
            {
                throw new InvalidOperationException(Properties.Resources.MarkupAlreadyLoaded);
            }
            this._markup = _cache.LoadMarkupFromFile(path, GetType().FullName);

            _loaded = true;
        }

        protected void LoadMarkupFromString(string markup)
        {
            if (_loaded == true)
            {
                throw new InvalidOperationException(Properties.Resources.MarkupAlreadyLoaded);
            }
            this._markup = _cache.CreateMarkup(markup, GetType().FullName);

            _loaded = true;
        }

        /// <summary>
        /// Replaces the key with the specified value.
        /// </summary>
        /// <param name="key">The key to replace. Cannot be null or
        /// empty or contains only blanks.</param>
        /// <param name="value">The value string. Cannot be null (Nothing in Visual Basic).</param>
        /// <exception cref="ArgumentNullException">
        /// 	<para><paramref name="key"/> is null (Nothing in Visual Basic).</para>
        /// 	<para>- or -</para>
        /// 	<para><paramref name="value"/> is null (Nothing in Visual Basic).</para>
        /// </exception>
        /// <exception cref="ArgumentException"><paramref name="key"/> is empty or 
        /// contains only blanks.</exception>
        /// <remarks>If the markup contains a value with the following pattern #{[KEY]} it can
        /// be replaced with this method.</remarks>
        [ParamNotNullOrEmpty("key"), ParamNotNull("value")]
        public void SetValue(string key, string value)
        {
            Markup.SetValue(key, value);
        }

        /// <summary>
        /// Replaces the key with the specified value, which is formatted with the specified arguments.
        /// </summary>
        /// <param name="key">The key to replace. Cannot be null or
        /// empty or contains only blanks.</param>
        /// <param name="value">The value string. Cannot be null (Nothing in Visual Basic).</param>
        /// <param name="args">The arguments for string formatting.</param>
        /// <exception cref="ArgumentNullException">
        /// 	<para><paramref name="key"/> is null (Nothing in Visual Basic).</para>
        /// 	<para>- or -</para>
        /// 	<para><paramref name="value"/> is null (Nothing in Visual Basic).</para>
        /// </exception>
        /// <exception cref="ArgumentException"><paramref name="key"/> is empty or
        /// contains only blanks.</exception>
        /// <remarks>If the markup contains a value with the following pattern #{[KEY]} it can
        /// be replaced with this method.</remarks>
        [ParamNotNullOrEmpty("key"), ParamNotNull("value")]
        public void SetValue(string key, string value, params object[] args)
        {
            Markup.SetValue(key, value, args);
        }

        /// <summary>
        /// Attaches an control to the markup.
        /// </summary>
        /// <param name="key">The key of the tag, where to attach the control to. Cannot be null or
        /// empty or contains only blanks.</param>
        /// <param name="control">The control to attach.</param>
        /// <exception cref="ArgumentNullException">
        /// 	<para><paramref name="key"/> is null (Nothing in Visual Basic).</para>
        /// 	<para>- or -</para>
        /// 	<para><paramref name="control"/> is null (Nothing in Visual Basic).</para>
        /// </exception>
        /// <exception cref="ArgumentException"><paramref name="key"/> is a string
        /// of length 0 or contains only blanks.</exception>
        [ParamNotNullOrEmpty("key"), ParamNotNull("control")]
        public void Attach(string key, Control control)
        {
            Markup.Attach(key, control);
        }

        /// <summary>
        /// Attaches an control to the markup.
        /// </summary>
        /// <param name="key">The key of the tag, where to attach the control to. Cannot be null or
        /// empty or contains only blanks.</param>
        /// <param name="control">The control to attach.</param>
        /// <param name="throwExceptions">if set to <c>true</c> the method throws an exception, if the
        /// key cannot been found.</param>
        /// <exception cref="ArgumentNullException">
        /// 	<para><paramref name="key"/> is null (Nothing in Visual Basic).</para>
        /// 	<para>- or -</para>
        /// 	<para><paramref name="control"/> is null (Nothing in Visual Basic).</para>
        /// </exception>
        /// <exception cref="ArgumentException"><paramref name="key"/> is a string
        /// of length 0 or contains only blanks.</exception>
        [ParamNotNullOrEmpty("key"), ParamNotNull("control")]
        public void Attach(string key, Control control, bool throwExceptions)
        {
            Markup.Attach(key, control, throwExceptions);
        }

        /// <summary>
        /// Hides the tag with the specified key.
        /// </summary>
        /// <param name="key">The key of the tag to hide. Cannot be null or
        /// empty or contains only blanks.</param>
        /// <exception cref="ArgumentNullException"><paramref name="key"/> is null 
        /// (Nothing in Visual Basic).</exception>
        /// <exception cref="ArgumentException"><paramref name="key"/> is a string
        /// of length 0 or contains only blanks.</exception>
        [ParamNotNullOrEmpty("key")]
        public void Hide(string key)
        {
            Markup.Hide(key);
        }

        /// <summary>
        /// Hides the tag with the specified key.
        /// </summary>
        /// <param name="key">The key of the tag to hide. Cannot be null or
        /// empty or contains only blanks.</param>
        /// <param name="throwExceptions">if set to <c>true</c> the method throws an exception, if the
        /// key cannot been found.</param>
        /// <exception cref="ArgumentNullException"><paramref name="key"/> is null
        /// (Nothing in Visual Basic).</exception>
        /// <exception cref="ArgumentException"><paramref name="key"/> is a string
        /// of length 0 or contains only blanks.</exception>
        [ParamNotNullOrEmpty("key")]
        public void Hide(string key, bool throwExceptions)
        {
            Markup.Hide(key, throwExceptions);
        }

        public bool IsMarkupInCache()
        {
            return _cache.HasInCache(GetType().FullName);
        }

        private void InjectContextValues(PageContext context)
        {
            Injection.InjectValue(this, ActionKey, context.Action);
            Injection.InjectValue(this, ApplicationPathKey, context.ApplicationPath);
            Injection.InjectValue(this, UrlKey, context.Url);
        }
    }
}
